/* typehints:start */
import { StorageInterface } from "./core/storage/interface";
/* typehints:end */

import { clamp, waitNextFrame } from "./core/utils/utils";
import { createLogger, logSection } from "./core/utils/logging";
import { IS_DEBUG, IS_MOBILE } from "./config/global";
import { StateManager } from "./core/state_manager";
import { PreloadState } from "./states/preload";
import { AnimationFrame } from "./core/animation_frame";
import { LocalStorage } from "./core/storage/localStorage";
import { MainMenuState } from "./states/main_menu";
import { setGlobalApp } from "./core/globals";
import { SoundImplHowler } from "./core/sound/howler";
import { ResourceLoader } from "./core/resource_loader";
import { Setting } from "./core/setting/settings";
import { PlatformInterface } from "./core/platform/interface";
import { PlusPlatform } from "./core/platform/plus";
import { BrowerPlatform } from "./core/platform/brower";
import { Api } from "./core/api";
import { ChangelogState } from "./states/changelog";
import { SettingState } from "./states/setting";
import { AboutState } from "./states/about";
import { SaveGame } from "./game/savegame";
import { GameCreateState } from "./states/game_create";
import { GameState } from "./states/game";
import { IndexedDB } from "./core/storage/indexedDB";
import { MapEditorState } from "./states/map_editor";

const logger = createLogger("Application");

let pageHiddenPropName, pageVisibilityEventName;
if (typeof document.hidden !== "undefined") {
  pageHiddenPropName = "hidden";
  pageVisibilityEventName = "visibilitychange";
} else if (typeof document.msHidden !== "undefined") {
  pageHiddenPropName = "msHidden";
  pageVisibilityEventName = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
  pageHiddenPropName = "webkitHidden";
  pageVisibilityEventName = "webkitvisibilitychange";
}

export class Application {
  constructor() {
    setGlobalApp(this);
    // 页面管理器
    this.stateMgr = new StateManager(this);
    // 动画器
    this.ticker = new AnimationFrame();
    // 音效
    this.sound = new SoundImplHowler(this);
    // 资源加载器
    this.resourceLoader = new ResourceLoader(this);
    // 设置
    this.setting = new Setting(this);
    // API
    this.api = new Api(this);
    // 游戏数据类
    this.savegame = new SaveGame(this);

    // 存储
    /** @type {StorageInterface} */
    this.storage = null;

    // 平台
    /** @type {PlatformInterface} */
    this.platform = null;

    this.screenWidth = 0;
    this.screenHeight = 0;

    // 程序是否在用户的监视下
    this.pageVisible = true;

    // 是否执行关闭程序的监听
    this.unloaded = false;

    // 保存最后一次重置窗口大小
    this.lastResizeCheck = null;
  }

  boot() {
    console.log("Booting...");
    this.checkResize(true);
    // 注册事件
    this.registerEventListeners();
    // 注册所有的状态
    this.registerStates();
    // 实例化存储类
    this.loadStorage();
    // 实例化平台类
    this.loadPlatform();

    this.stateMgr.moveToState("PreloadState");

    this.ticker.frameEmitted.add(this.onFrameEmitted, this);
    this.ticker.start();
  }

  loadStorage() {
    if(G_IS_DEV || !window.indexedDB){
      this.storage = new LocalStorage(this)
    } else {
      this.storage = new IndexedDB(this);
    }
    // this.storage = window.indexedDB
    //   ? new IndexedDB(this)
    //   : new LocalStorage(this);
  }

  loadPlatform(platform) {
    if (platform === "plus") {
      this.platform = new PlusPlatform(this);
    } else {
      this.platform = new BrowerPlatform(this);
    }
  }

  registerStates() {
    let states = [
      PreloadState,
      MainMenuState,
      ChangelogState,
      SettingState,
      AboutState,
      GameCreateState,
      GameState,
    ];

    if(G_IS_DEV){
      states = states.concat([
        MapEditorState
      ])
    }

    states.map((state) => {
      this.stateMgr.register(state);
    });
  }

  registerEventListeners() {
    window.addEventListener("resize", () => {
      this.checkResize()
    }, true);
    window.addEventListener(
      "orientationchange",
      () => this.checkResize(),
      true
    );

    // 离开页面或切换到后台
    document.addEventListener(
      pageVisibilityEventName,
      this.handleVisibilityChange.bind(this),
      false
    );

    // 关闭程序
    window.addEventListener(
      "beforeunload",
      this.onBeforeUnload.bind(this),
      true
    );
    window.addEventListener("unload", this.onUnload.bind(this), true);
  }

  isRenderable() {
    return this.pageVisible;
  }

  onFrameEmitted(dt) {
    if (!this.isRenderable()) {
      return;
    }

    const time = performance.now();

    if (!this.lastResizeCheck || time - this.lastResizeCheck > 1000) {
      this.checkResize();
      this.lastResizeCheck = time;
    }

    const currentState = this.stateMgr.currentState;
    if (currentState) {
      currentState.onRender(dt);
    }
  }

  onBeforeUnload(ev) {
    logSection("关闭程序前监听", "#f77");
    const currentState = this.stateMgr.currentState;

    if (currentState && currentState.getHasUnloadConfirmation()) {
      ev.preventDefault();
      ev.returnValue = "你确定退出游戏？";
    }
  }

  onUnload() {
    if (!this.unloaded) {
      logSection("关闭程序监听", "#f77");
      this.unloaded = true;
      const currentState = this.stateMgr.currentState;
      if (currentState) {
        currentState.onBeforeExit();
      }
    }
  }

  handleVisibilityChange(ev) {
    const pageVisible = !document[pageHiddenPropName];
    if (pageVisible !== this.pageVisible) {
      this.pageVisible = pageVisible;
      logger.log("显示切换:", this.pageVisible ? "显示" : "隐藏");
      this.onAppRenderableStateChanged(this.pageVisible);
    }
  }

  onAppRenderableStateChanged(renderable) {
    const currentState = this.stateMgr.currentState;
    if (!renderable) {
      if (currentState) {
        currentState.onAppPause();
      }
    } else {
      if (currentState) {
        currentState.onAppResume();
      }
      this.checkResize();
    }

    this.sound.onPageRenderableStateChanged(renderable);
  }

  /**
   * 更新页面UI尺寸
   * @param {*} forceUpdate
   */
  checkResize(forceUpdate = false) {
    const w = window.innerWidth;
    const h = window.innerHeight;

    if (this.screenWidth !== w || this.screenHeight !== h || forceUpdate) {
      this.screenWidth = w;
      this.screenHeight = h;

      const currentState = this.stateMgr.currentState;

      if (currentState) {
        currentState.onResized(this.screenWidth, this.screenHeight);
      }

      const scale = this.getEffectiveUiScale();
      waitNextFrame().then(() => {
        document.documentElement.style.setProperty("--ui-scale", `${scale}`);
      });
    }
  }

  // 重设字体
  changeFontFamily(){
    let font = this.setting.currentData.fontFamily;
    document.documentElement.className = font || 'GameFont';
  }

  getEffectiveUiScale() {
    // if(IS_MOBILE){
    //   return 1;
    // }

    const avgDims = Math.min(this.screenWidth, this.screenHeight);
    return (
      clamp((avgDims / 1000) * 2, 1, 10) * this.setting.getInterfaceScaleValue()
    );
  }
}
